package cn.hbads.support.structure.result;

import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * <b>计算结果封装对象<b/>
 *
 * @author yvvb
 * @date 2025/5/4
 */
public class ComputeResult<T, E> {
    /**
     * 计算是否成功
     */
    private final boolean isSuccess;
    /**
     * 计算成功后得到的计算结果
     */
    private final T result;
    /**
     * 计算失败后得到的失败模型
     */
    private final E failedModel;
    /**
     * 计算失败的异常原因(可以没有)
     */
    private final Throwable cause;

    private ComputeResult(boolean isSuccess, T result, E failedModel, Throwable cause) {
        this.isSuccess = isSuccess;
        this.result = result;
        this.failedModel = failedModel;
        this.cause = cause;
    }

    /**
     * 静态计算成功对象构建方法
     *
     * @param result T 计算结果
     *
     * @return ComputeResult<T, E> 计算封装对象
     */
    public static <T, E> ComputeResult<T, E> success(T result) {
        return new ComputeResult<>(true, result, null, null);
    }

    /**
     * 静态计算失败对象构建方法
     *
     * @param failedModel E 失败响应模型
     *
     * @return ComputeResult<T, E> 计算封装对象
     */
    public static <T, E> ComputeResult<T, E> fail(E failedModel) {
        return new ComputeResult<>(false, null, failedModel, null);
    }

    /**
     * 静态计算失败对象构建方法
     *
     * @param failedModel E 失败响应模型
     * @param cause       Throwable 导致计算失败的异常对象
     *
     * @return ComputeResult<T, E> 计算封装对象
     */
    public static <T, E> ComputeResult<T, E> fail(E failedModel, Throwable cause) {
        return new ComputeResult<>(false, null, failedModel, cause);
    }

    //-----一些方便进行结果处理的方法（借鉴Rust的Result枚举和Java's Optional流）----

    public T get() {
        return result;
    }

    public T unwrapped() throws Throwable {
        if (this.isSuccess) {
            return result;
        } else {
            throw cause;
        }
    }

    public <X extends Throwable> T unwrapped(Function<E, X> errorMsgFunction) throws X {
        if (this.isSuccess) {
            return result;
        } else {
            throw errorMsgFunction.apply(failedModel);
        }
    }

    public T orElse(T other) {
        return isSuccess ? result : other;
    }

    public <X extends Throwable> T orElseThrow(BiFunction<E, Throwable, X> errorMsgFunction) throws X {
        if (this.isSuccess) {
            return result;
        } else {
            throw errorMsgFunction.apply(failedModel, cause);
        }
    }

    public T orElseSupplier(Supplier<T> supplier) {
        if (this.isSuccess) {
            return result;
        } else {
            return supplier.get();
        }
    }

    public T orElseToResult(BiFunction<E, Throwable, T> supplier) {
        if (this.isSuccess) {
            return result;
        } else {
            return supplier.apply(failedModel, cause);
        }
    }

    public Optional<T> toOptional() {
        return Optional.ofNullable(result);
    }

    public <R> Optional<R> map(Function<T, R> function) {
        return this.toOptional().map(function);
    }

    public <R> ComputeResult<R, E> mapErr(Function<T, R> function) {
        if (isSuccess) {
            return ComputeResult.success(function.apply(this.result));
        } else {
            return ComputeResult.fail(this.failedModel, this.cause);
        }
    }

    public void ifFail(Consumer<E> failConsumer) {
        if (isSuccess) {
            failConsumer.accept(failedModel);
        }
    }

    public boolean isSuccess() {
        return isSuccess;
    }

    public void ifSuccess(Consumer<T> consumer) {
        if (isSuccess) {
            consumer.accept(result);
        }
    }

    public void ifSuccessOrFail(Consumer<T> successConsumer, Consumer<E> failureConsumer) {
        if (isSuccess) {
            successConsumer.accept(result);
        } else {
            failureConsumer.accept(failedModel);
        }
    }


}
